﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CoreRay
{
    public class RaySlope
    {
        public RaySlope(Ray ray)
        {
            // Compute inverse ray directions
            this.InverseDirectionX = 1.0f / ray.Direction.X;
            this.InverseDirectionY = 1.0f / ray.Direction.Y;
            this.InverseDirectionZ = 1.0f / ray.Direction.Z;

            // Compute ray slopes
            this.SlopeYX = ray.Direction.X * InverseDirectionY;
            this.SlopeXY = ray.Direction.Y * InverseDirectionX;
            this.SlopeZY = ray.Direction.Y * InverseDirectionZ;
            this.SlopeYZ = ray.Direction.Z * InverseDirectionY;
            this.SlopeZX = ray.Direction.X * InverseDirectionZ;
            this.SlopeXZ = ray.Direction.Z * InverseDirectionX;

            // Compute bounding box precomputation constants
            this.BoundingBoxConstantXY = ray.Origin.Y - SlopeXY * ray.Origin.X;
            this.BoundingBoxConstantXZ = ray.Origin.Z - SlopeXZ * ray.Origin.X;
            this.BoundingBoxConstantYX = ray.Origin.X - SlopeYX * ray.Origin.Y;
            this.BoundingBoxConstantYZ = ray.Origin.Z - SlopeYZ * ray.Origin.Y;
            this.BoundingBoxConstantZX = ray.Origin.X - SlopeZX * ray.Origin.Z;
            this.BoundingBoxConstantZY = ray.Origin.Y - SlopeZY * ray.Origin.Z;
    
            // Find ray slope type
            this.SlopeType = FindRaySlopeType(ray);
        }

        public float InverseDirectionX
        {
            get;
            private set;
        }

        public float InverseDirectionY
        {
            get;
            private set;
        }

        public float InverseDirectionZ
        {
            get;
            private set;
        }

        public float SlopeYX
        {
            get;
            private set;
        }

        public float SlopeXY
        {
            get;
            private set;
        }

        public float SlopeZY
        {
            get;
            private set;
        }

        public float SlopeYZ
        {
            get;
            private set;
        }

        public float SlopeXZ
        {
            get;
            private set;
        }

        public float SlopeZX
        {
            get;
            private set;
        }

        public float BoundingBoxConstantXY
        {
            get;
            private set;
        }

        public float BoundingBoxConstantXZ
        {
            get;
            private set;
        }

        public float BoundingBoxConstantYX
        {
            get;
            private set;
        }

        public float BoundingBoxConstantYZ
        {
            get;
            private set;
        }

        public float BoundingBoxConstantZX
        {
            get;
            private set;
        }

        public float BoundingBoxConstantZY
        {
            get;
            private set;
        }

        public RaySlopeType SlopeType
        {
            get;
            private set;
        }

        private RaySlopeType FindRaySlopeType(Ray ray)
        {
            RaySlopeType slopeType;

            //ray slope classification
            if (ray.Direction.X < 0)
            {
                if (ray.Direction.Y < 0)
                {
                    if (ray.Direction.Z < 0)
                    {
                        slopeType = RaySlopeType.MMM;
                    }
                    else if (ray.Direction.Z > 0)
                    {
                        slopeType = RaySlopeType.MMP;
                    }
                    else
                    {
                        slopeType = RaySlopeType.MMO;
                    }
                }
                else
                {
                    if (ray.Direction.Z < 0)
                    {
                        slopeType = RaySlopeType.MPM;

                        if (ray.Direction.Y == 0)
                        {
                            slopeType = RaySlopeType.MOM;
                        }
                    }
                    else
                    {
                        if ((ray.Direction.Y == 0) && (ray.Direction.Z == 0))
                            slopeType = RaySlopeType.MOO;
                        else if (ray.Direction.Z == 0)
                            slopeType = RaySlopeType.MPO;
                        else if (ray.Direction.Y == 0)
                            slopeType = RaySlopeType.MOP;
                        else
                            slopeType = RaySlopeType.MPP;
                    }
                }
            }
            else
            {
                if (ray.Direction.Y < 0)
                {
                    if (ray.Direction.Z < 0)
                    {
                        slopeType = RaySlopeType.PMM;
                        if (ray.Direction.X == 0)
                            slopeType = RaySlopeType.OMM;
                    }
                    else
                    {
                        if ((ray.Direction.X == 0) && (ray.Direction.Z == 0))
                            slopeType = RaySlopeType.OMO;
                        else if (ray.Direction.Z == 0)
                            slopeType = RaySlopeType.PMO;
                        else if (ray.Direction.X == 0)
                            slopeType = RaySlopeType.OMP;
                        else
                            slopeType = RaySlopeType.PMP;
                    }
                }
                else
                {
                    if (ray.Direction.Z < 0)
                    {
                        if ((ray.Direction.X == 0) && (ray.Direction.Y == 0))
                            slopeType = RaySlopeType.OOM;
                        else if (ray.Direction.X == 0)
                            slopeType = RaySlopeType.OPM;
                        else if (ray.Direction.Y == 0)
                            slopeType = RaySlopeType.POM;
                        else
                            slopeType = RaySlopeType.PPM;
                    }
                    else
                    {
                        if (ray.Direction.X == 0)
                        {
                            if (ray.Direction.Y == 0)
                                slopeType = RaySlopeType.OOP;
                            else if (ray.Direction.Z == 0)
                                slopeType = RaySlopeType.OPO;
                            else
                                slopeType = RaySlopeType.OPP;
                        }
                        else
                        {
                            if ((ray.Direction.Y == 0) && (ray.Direction.Z == 0))
                                slopeType = RaySlopeType.POO;
                            else if (ray.Direction.Y == 0)
                                slopeType = RaySlopeType.POP;
                            else if (ray.Direction.Z == 0)
                                slopeType = RaySlopeType.PPO;
                            else
                                slopeType = RaySlopeType.PPP;
                        }
                    }
                }
            }

            return slopeType;
        }
    }

    public enum RaySlopeType : byte
    {
        MMM, 
        MMP, 
        MPM, 
        MPP, 
        PMM, 
        PMP, 
        PPM, 
        PPP, 
        POO, 
        MOO, 
        OPO, 
        OMO, 
        OOP, 
        OOM,
        OMM, 
        OMP, 
        OPM, 
        OPP, 
        MOM, 
        MOP, 
        POM, 
        POP, 
        MMO, 
        MPO, 
        PMO, 
        PPO
    }
}
